package org.budo.dubbo.protocol.http.page.util;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;

import org.budo.dubbo.protocol.http.exception.BudoRuntimeException;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

/**
 * @author lmw
 */
public class GenericTypeHtmlUtil {
    private static final Logger log = Slf4j.getLogger();

    public static String typeLink(Type type) {
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            String str = parameterizedTypeLink(parameterizedType);

            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

            str += "&lt;";
            for (int i = 0; i < actualTypeArguments.length; i++) {
                str += typeLink(actualTypeArguments[i]);

                if (i < actualTypeArguments.length - 1) {
                    str += ",";
                }
            }
            str += "&gt;";

            return str;
        }

        if (type instanceof Class) {
            Class clazz = (Class) type;
            return classLink(clazz);
        }

        if (type instanceof TypeVariable || type instanceof WildcardType) {
            return type + "";
        }

        String message = "#47 type=" + type + ", class=" + type.getClass();
        throw new BudoRuntimeException(message);
    }

    private static String parameterizedTypeLink(ParameterizedType parameterizedType) {
        Type type = parameterizedType.getRawType();
        if (type instanceof Class) {
            return classLink((Class) type);
        }
        return type + "";
    }

    private static String classLink(Class clazz) {
        boolean isArray = clazz.isArray();

        if (isArray) {
            clazz = clazz.getComponentType();
        }

        String className = clazz.getCanonicalName();
        if (clazz.isPrimitive() || className.startsWith("java.lang.") || className.startsWith("java.util.") || className.startsWith("java.sql.")) {
            return clazz.getSimpleName() + (isArray ? "[]" : "");
        } else if (clazz.isEnum()) {

            if (!ReflectUtil.hasClass(className)) { // 嵌套枚举类名最后一个类名分割不是. 而是$
                int i = className.lastIndexOf('.');
                className = className.substring(0, i) + "$" + className.substring(i + 1);
            }

            return "<a title=\"枚举结构\" href=\"?action=type_structure&type=" + className + "\">" + clazz.getSimpleName() + "</a>" + (isArray ? "[]" : "");
        } else {
            return "<a title=\"类结构\" href=\"?action=type_structure&type=" + className + "\">" + clazz.getSimpleName() + "</a>" + (isArray ? "[]" : "");
        }
    }

    public static String[] typeLinks(Type[] genericParameterTypes) {
        String[] names = new String[genericParameterTypes.length];
        for (int i = 0; i < genericParameterTypes.length; i++) {
            names[i] = typeLink(genericParameterTypes[i]);
        }
        return names;
    }

    public static Boolean[] parameterTypeIsEnum(Method method) {
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        Boolean[] parameterTypeIsEnum = new Boolean[genericParameterTypes.length];

        for (int i = 0; i < parameterTypeIsEnum.length; i++) {
            Type parameterType = genericParameterTypes[i];
            if (parameterType instanceof TypeVariable || parameterType instanceof WildcardType) {
                parameterTypeIsEnum[i] = false;
                continue;
            }

            Class parameterClass;
            if (parameterType instanceof ParameterizedType) {
                parameterClass = parameterizedType(method, parameterType);
            } else {
                parameterClass = (Class) parameterType;
            }

            if (parameterClass.isArray()) {
                parameterClass = parameterClass.getComponentType();
            }

            parameterTypeIsEnum[i] = parameterClass.isEnum();
        }

        return parameterTypeIsEnum;
    }

    /**
     * 用以判断是否是枚举
     */
    private static Class parameterizedType(Method method, Type parameterType) {
        Type[] actualTypeArguments = ((ParameterizedType) parameterType).getActualTypeArguments();
        Type actualType_0 = actualTypeArguments[0];

        if (actualType_0 instanceof WildcardType) {
            // List<?>
            log.error("#129 method=" + method //
                    + ", actualType_0=" + actualType_0 //
                    + ", lowerBounds=" + StringUtil.join(((WildcardType) actualType_0).getLowerBounds())//
                    + ", upperBounds=" + StringUtil.join(((WildcardType) actualType_0).getUpperBounds()));
            return Object.class;
        }

        if (actualType_0 instanceof TypeVariable) {
            log.error("#138 method=" + method //
                    + ", actualType_0=" + actualType_0 //
                    + ", lowerBounds=" + StringUtil.join(((TypeVariable) actualType_0).getBounds()));
            return Object.class;
        }

        if (actualType_0 instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) actualType_0;
            log.warn("#145 method=" + method //
                    + ", actualType_0=" + actualType_0 //
                    + ", rawType=" + parameterizedType.getRawType() //
                    + ", ownerType=" + parameterizedType.getOwnerType() //
                    + ", actualTypeArguments=" + StringUtil.join(parameterizedType.getActualTypeArguments()));
            return Object.class;
        }

        if (actualType_0 instanceof Class) {
            return (Class) actualType_0;
        }

        throw new IllegalStateException("#147 method=" + method + ", parameterType=" + parameterType + ", actualType_0=" + actualType_0 + ", actualType_0.type=" + actualType_0.getClass());
    }
}